
package IC.Lexer;

%%

%class Lexer
%public
%function next_token
%type Token
%line
%column
%scanerror LexicalError

%{
  StringBuffer string = new StringBuffer();
  boolean inComment = false;
  int cmtline = 0;
%}

LineTerminator = \r|\n|\r\n
InputCharacter = [^\r\n]

ALPHA=[A-Za-z]
DIGIT=[0-9]
NONNEWLINE_WHITE_SPACE_CHAR=[\ \t\b\012]
NEWLINE=\r|\n|\r\n
WHITE_SPACE_CHAR=[\n\r\ \t\b\012]

STRING_TEXT = [^\r\n\"\\] 

/* STRING_TEXT=(\\\"|[^\n\r\"]|\\{WHITE_SPACE_CHAR}+\\)* */

Comment = {TraditionalComment} | {EndOfLineComment} | 
          {DocumentationComment}
          
TraditionalComment = "/*" [^*] ~"*/" | "/*" "*"+ "/"
EndOfLineComment = "//" {InputCharacter}* {LineTerminator}?
DocumentationComment = "/*" "*"+ [^/*] ~"*/"

String=\"{STRING_TEXT}\"

Ident = [a-z]([A-Za-z0-9_])*
ClassIdent = [A-Z]([A-Za-z0-9_])*

Integer = 0 | [1-9][0-9]*

XXX = {InputCharacter}* {LineTerminator}?

%state STRING, CLASS, COMMENT

%%
<YYINITIAL> {

	"break" { return new Token(sym.BREAK, yyline); }
	
	"continue" { return new Token(sym.CONTINUE, yyline); }
	"extends" { return new Token(sym.EXTENDS, yyline); }
	"else" { return new Token(sym.ELSE, yyline); }
	"if" { return new Token(sym.IF, yyline); }
	
	"length" { return new Token(sym.LENGTH, yyline); }
	"new" { return new Token(sym.NEW, yyline); }
	"return" { return new Token(sym.RETURN, yyline); }
	"static" { return new Token(sym.STATIC, yyline); }
	"this" { return new Token(sym.THIS, yyline); }
	"void" { return new Token(sym.VOID, yyline); }
	"while" { return new Token(sym.WHILE, yyline); }
	
	"int" { return new Token(sym.INT, yyline); }
	"boolean" { return new Token(sym.BOOLEAN, yyline); }
	"string" { return new Token(sym.STRING, yyline); }
	
	"true" { return new Token(sym.TRUE, new Boolean(true), yyline); }
	"false" { return new Token(sym.FALSE, new Boolean(false), yyline); }
	"null" { return new Token(sym.NULL, yyline); }
	
	"=" { return new Token(sym.ASSIGN, yyline); }
	"," { return new Token(sym.COMMA, yyline); }
	"/" { return new Token(sym.DIVIDE, yyline); }
	"." { return new Token(sym.DOT, yyline); }
	"==" { return new Token(sym.EQUAL, yyline); }
	">" { return new Token(sym.GT, yyline); }
	">=" { return new Token(sym.GTE, yyline); }
	"&&" { return new Token(sym.LAND, yyline); }
	"[" { return new Token(sym.LB, yyline); }
	"(" { return new Token(sym.LP, yyline); }
	"{" { return new Token(sym.LCBR, yyline); }
	"!" { return new Token(sym.LNEG, yyline); }
	"||" { return new Token(sym.LOR, yyline); }
	"<" { return new Token(sym.LT, yyline); }
	"<=" { return new Token(sym.LTE, yyline); }
	"-" { return new Token(sym.MINUS, yyline); }
	"%" { return new Token(sym.MOD, yyline); }
	"*" { return new Token(sym.MULTIPLY, yyline); }
	"!=" { return new Token(sym.NEQUAL, yyline); }
	"+" { return new Token(sym.PLUS, yyline); }
	"]" { return new Token(sym.RB, yyline); }
	"}" { return new Token(sym.RCBR, yyline); }
	")" { return new Token(sym.RP, yyline); }
	";" { return new Token(sym.SEMI, yyline); }
	
	/* string  */
	\"  { yybegin(STRING); string.setLength(0); }
	
	"class"{WHITE_SPACE_CHAR}+ { yybegin(CLASS); return new Token(sym.CLASS, yyline); }
	
	{ClassIdent} { return new Token(sym.CLASS_ID,yytext(), yyline); }    /* i.e. xx.print() */
	{Ident} { return new Token(sym.ID, yytext(), yyline); }
	
	{Integer} { 
		Integer i;
		try {
			i = Integer.parseInt(yytext());
		} catch(NumberFormatException e) {
			throw new LexicalError(
  				LexicalError.formatErrorMessage(yyline+1, "Illegal Integer value \""+yytext()+"\"" )
  			); 
		}
		return new Token(sym.INTEGER, i, yyline); 
	}
	
	{WHITE_SPACE_CHAR}+ { /* ignore */ }
	{Comment}  { /* ignore */ }

	"/*" { inComment = true; cmtline = yyline; yybegin(COMMENT); }
	
}

<COMMENT> {
	"*/" { inComment = false; yybegin(YYINITIAL);}
	{XXX}+ {   }
	/*
		. { 
			throw new LexicalError(
	  			LexicalError.formatErrorMessage(yyline+1, "Unclosed Comment")
	  		); 
		}
	*/
}

<CLASS> {

	{ClassIdent} { 
		yybegin(YYINITIAL);
		return new Token(sym.CLASS_ID,  yytext(), yyline); 
	}
	
}


<STRING> {
  \"   { yybegin(YYINITIAL); return new Token(sym.QUOTE, string.toString(), yyline); }
  
  {STRING_TEXT}+             { string.append( yytext() ); }
  
  /* escape sequences */
  \\t                         { string.append( '\t' ); }
  \\n                         { string.append( '\n' ); }
  \\\"                        { string.append( '\"' ); }
  \\\\						  { string.append( '\\' ); }
  
  /* error cases */
  
  \\. { 
  	throw new LexicalError(
  		LexicalError.formatErrorMessage(yyline+1, "Illegal escape sequence \""+yytext()+"\"" )
  	); 
  }
  {LineTerminator} { 
  	throw new LexicalError(
  		LexicalError.formatErrorMessage(yyline+1, "Unterminated string at end of line") ); 
  }
}

. {
  throw new LexicalError(
  	LexicalError.formatErrorMessage(yyline+1, "Illegal character '"+yytext()+ "'")
  ); 
}

<<EOF>>        
	{ 
		if(inComment) {
			throw new LexicalError(
  				LexicalError.formatErrorMessage(cmtline+1, "Unclosed Comment")
  			);
		}
		else {
			return new Token(sym.EOF,yyline); 
		}
	}
